﻿using System;
using System.Collections;
using System.Diagnostics;
using widgetLauncher.Properties;
using wdwp.Desktop.widgetLib.CommonTypes;

namespace wdwp.Desktop.Launcher
{

    /// <summary>
    /// 실행중인 위젯들을 관리
    /// </summary>
    internal class WidgetProcManager
    {
        /// <summary>
        /// 위젯이 시작될때 발생하는 메세지
        /// </summary>
        public const string MSG_START_WIDGET = "Run_newWidget";
        /// <summary>
        /// 위젯이 죽을때 발생하는 메세지
        /// </summary>
        public const string MSG_KILL_WIDGET = "Kill_runWidget";
        /// <summary>
        /// 위젯이 리프레시 될때 발생하는 메세지
        /// </summary>
        public const string MSG_LIST_REFRESH = "Refresh_List";

        /// <summary>
        /// 현재 실행중인 위젯의 프로세스 리스트
        /// </summary>
        ArrayList m_arProcList; //프로세스 리스트
        /// <summary>
        /// 위젯이 종료될 때까지 기다릴 것인가를 결정하는 플래그 (위젯을 강제로 죽일 것인가 아니면 종료될 때까지 기다릴 것인가)
        /// </summary>
        bool m_bWaitUntillQuit;     //위젯이 종료될 때까지 기다릴 것인가?


        public delegate void procManagerEventHandler(object sender, WidgetEventArgs e);
        /// <summary>
        /// 위젯 프로세스 리스트가 변경되었음을 알림
        /// </summary>
        public static event procManagerEventHandler ProcListChanged;

        /// <summary>
        /// 프로세스 관리자 실행 
        /// </summary>
        public WidgetProcManager()
        {
            m_bWaitUntillQuit = true;
            m_arProcList = new ArrayList();
        }

        /// <summary>
        /// 새 위젯 실행 
        /// </summary>
        /// <param name="SrcInfo">위젯을 실행하는데 필요한 소스정보</param>
        /// <returns>정상적으로 위젯이 생성되면 프로세스 반환 아니면 예외발생</returns>
        public Process RunNewWidget(sWidgetInfo SrcInfo)
        {
            try
            {
                //같은 위젯이 실행중인지 검사
                foreach (sWidgetProc aProc in m_arProcList)
                {
                    if (aProc.SrcInfo == SrcInfo)
                    {                      
                        throw new Exception("이미 같은 위젯이 실행중입니다.");
                    }
                }


                //아니라면 위젯을 실행 
                //string Args = string.Format("/s:uri=\"{0}\" /s:size=\"{1},{2}\" /s:name=\"{3}\" ", SrcInfo.FullHTMLUri, SrcInfo.Width, SrcInfo.Height, SrcInfo.Name);
                string Args = string.Format("/s:file={0}", Settings.Default.LoaderStartupFileName);

                //Process newProc = Process.Start("widgetLoader.exe", SrcInfo.FullHTMLUri);
                ProcessStartInfo psi = new ProcessStartInfo("widgetLoader.exe", Args);
                psi.UseShellExecute = false;

                Process newProc = new Process();
                newProc.StartInfo = psi;                                
                newProc.EnableRaisingEvents = true;
                newProc.Exited += new EventHandler(WidgetProc_Exited);

                newProc.Start();                
                
                sWidgetProc newWidget = new sWidgetProc(SrcInfo, newProc.Id);
                m_arProcList.Add(newWidget);

                ProcListChanged(this, new WidgetEventArgs(MSG_START_WIDGET, " 실행됨", newWidget));

                return newProc;
            }
            catch (Exception exc)
            {
                Debug.WriteLine("Cannot Create a new process : " + exc.ToString());
                throw exc;
            }
                  
        }

        /// <summary>
        /// 위젯 정보로 위젯 프로세스를 죽임 
        /// </summary>
        /// <param name="srcInfo">위젯 소스 정보</param>
        /// <returns>죽이기에 성공하면 true 아니면 예외와 함께 false</returns>
        public void KillRunWidget(sWidgetInfo srcInfo)
        {
            try
            {
                foreach (sWidgetProc aWdgtProc in m_arProcList)
                {
                    if (aWdgtProc.SrcInfo.Equals(srcInfo))
                    {
                        KillRunWidget(aWdgtProc.procId);
                    }
                }
            }
            catch (Exception exc)
            {
                throw exc;
               
            }            
        }
               

        /// <summary>
        /// 위젯 정보로 위젯 프로세스를 죽임
        /// </summary>
        /// <param name="widgetPID">죽이고 싶은 위젯 pID</param>
        /// <returns>죽이기에 성공하면 true 아니면 예외와 함께 false</returns>
        public void KillRunWidget(int widgetPID)
        {
            try
            {
                Process aProcess = Process.GetProcessById(widgetPID);

                if (m_bWaitUntillQuit)
                {
                    aProcess.CloseMainWindow();                   
                }
                else
                {
                    aProcess.Kill();
                }

                aProcess.Close();
               
            }
            catch (ArgumentException aexc)
            {                
                throw aexc;
            }
            catch (Exception exc)
            {
                throw exc;
            }
            finally
            {
                //프로세스가 이미 죽어 프로세스 리스트가 바뀌었음으로 이를 알려준다. 
                sWidgetProc proc = GetProcByPID(widgetPID);

                if (!proc.Empty)
                    m_arProcList.Remove(proc);

                ProcListChanged(this, new WidgetEventArgs(MSG_KILL_WIDGET, " 종료됨", proc));
                                
            }
                       
        }


        /// <summary>
        /// 해당 위젯이 로드되었는지 확인한다.
        /// </summary>
        /// <param name="aProc">위젯 프로세스 정보</param>
        /// <returns>정상적으로 발견되면 true, 아니면 false</returns>
        public bool IsWidgetLoaded(sWidgetProc aProc)
        {
            try
            {
                foreach (sWidgetProc aWdgtProc in m_arProcList)
                {
                    if (aWdgtProc == aProc)
                    {   
                        return true;
                    }
                }

                return false;
            }
            catch (Exception exc)
            {
                throw exc;

            }

        }


        /// <summary>
        /// 해당 위젯이 로드되었는지 확인한다.
        /// </summary>
        /// <param name="aSrc">위젯 소스 정보</param>
        /// <returns>정상적으로 발견되면 true, 아니면 false</returns>
        public bool IsWidgetLoaded(sWidgetInfo aSrc)
        {
            try
            {
                foreach (sWidgetProc aWdgtProc in m_arProcList)
                {
                    if (aWdgtProc.SrcInfo == aSrc)
                    {
                        return true;
                    }
                }

                return false;
            }
            catch (Exception exc)
            {
                throw exc;
            }
        }

        
        /// <summary>
        /// 현재 실행중인 모든 위젯을 죽임 
        /// </summary>
        /// <returns>모든 위젯 프로세스가 종료되면 true, 그렇지 않으면 예외를 출력</returns>
        public void KillAllWidgets()
        {
            try
            {
                ArrayList tempArray = m_arProcList.Clone() as ArrayList;

                foreach (sWidgetProc aWdgtProc in tempArray)
                {
                    KillRunWidget(aWdgtProc.procId);                    
                }

                //아직 안죽은 위젯이 있다.-> 예외
                if (m_arProcList.Count > 0)
                {
                    throw new Exception("같이 종료되지 않은 위젯이 있습니다. 이것들은 직접 종료해야 합니다.");
                }
            }
            catch (Exception exc)
            {
                throw exc;
            }
        }


        /// <summary>
        /// 매니저 정보 리프레시
        /// </summary>
        public void Refresh()
        {
            ProcListChanged(this, new WidgetEventArgs(MSG_LIST_REFRESH, "리스트 리프레시"));
        }


        /// <summary>
        /// Process 정보들이 담긴 리스트를 반환 
        /// </summary>
        public ArrayList ProcessList 
        {
            get
            {
                return m_arProcList;
            }            
        }

        /// <summary>
        /// 위젯이 종료될 때까지 기다릴것인지 결정 false로 세팅하면 그냥 죽임 
        /// </summary>
        public bool WaitUntillQuit 
        {
            get
            {
                return m_bWaitUntillQuit;
            }

            set
            {
                m_bWaitUntillQuit = value;
            }
        }


        /// <summary>
        /// 프로세스 리스트에서 PID를 바탕으로 인덱스를 얻어온다.
        /// </summary>
        /// <param name="PID">해당 프로세스 아이디</param>
        /// <returns> PID와 매칭되는 인덱스를 지닌 리스트 번호를 반환, 없으면 -1을 반환</returns>

        private int GetIndexOfListBy(int PID)
        {
            foreach (sWidgetProc aProc in m_arProcList)
            {
                if (aProc.procId == PID)
                {
                    return m_arProcList.IndexOf(aProc);
                }            
            }

            return -1;
        }


        /// <summary>
        /// 프로세스 리스트에서 PID를 바탕으로 프로세스 정보(sWidgetProc)를 얻어온다. 
        /// </summary>
        /// <param name="PID">해당 프로세스 아이디</param>
        /// <returns></returns>
        private sWidgetProc GetProcByPID(int PID)
        {
            int procIndex = GetIndexOfListBy(PID);
            sWidgetProc result;

            if (procIndex >= 0)
            {
                result = (sWidgetProc)m_arProcList[procIndex];
            }
            else
            {
                result = new sWidgetProc();
                result.Empty = true;                
            }

            return result;
        }


        /// <summary>
        /// 위젯 프로세스가 종료되면 발생하는 이벤트 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void WidgetProc_Exited(object sender, EventArgs e)
        {
            Process procKilled = sender as Process;            
            sWidgetProc proc = GetProcByPID(procKilled.Id);

            if (!proc.Empty)
            {
                m_arProcList.Remove(proc);
                //ProcListChanged(this, new WidgetEventArgs(MSG_KILL_WIDGET, proc));
            }
        }


    }


}